import { Client } from 'minio';
import { nanoid } from 'nanoid';
import { basename } from 'path';
import { logger, createErrorLogContext } from '../utils/logger.js';

interface UploadFileArgs {
  filePath: string;
  objectName: string;
}

interface UploadResult {
  success: boolean;
  url: string;
  presignedUrl: string;
  objectPath: string;
  objectName: string;
  message: string;
}

class MinioUploader {
  private client: Client | null = null;

  constructor() {
    this.initializeClient();
  }

  private initializeClient() {
    const endpoint = process.env.MINIO_ENDPOINT;
    const accessKey = process.env.MINIO_ACCESS_KEY;
    const secretKey = process.env.MINIO_SECRET_KEY;
    const useSSL = process.env.MINIO_USE_SSL === 'true';

    if (!endpoint || !accessKey || !secretKey) {
      logger.warn('Minio Uploader: Credentials not configured - upload functionality disabled', {
        hasEndpoint: !!endpoint,
        hasAccessKey: !!accessKey,
        hasSecretKey: !!secretKey,
      });
      return;
    }

    try {
      this.client = new Client({
        endPoint: endpoint,
        port: parseInt(process.env.MINIO_PORT || '9000'),
        useSSL: useSSL,
        accessKey: accessKey,
        secretKey: secretKey,
      });

      logger.info('Minio Uploader: Client initialized successfully', {
        endpoint,
        port: process.env.MINIO_PORT || '9000',
        useSSL,
      });
    } catch (error) {
      logger.error(
        'Minio Uploader: Failed to initialize client',
        createErrorLogContext(error, {
          endpoint,
        })
      );
      this.client = null;
    }
  }

  async uploadFile(args: UploadFileArgs): Promise<UploadResult> {
    const bucketName = process.env.MINIO_BUCKET;
    // Use default bucket from env if not provided
    if (!bucketName) {
      throw new Error('Bucket name is required (set MINIO_BUCKET in .env)');
    }

    const { filePath, objectName } = args;

    if (!this.client) {
      throw new Error('Minio client not configured. Please check environment variables.');
    }

    try {
      const file = Bun.file(filePath);
      const fileContent = await file.arrayBuffer();

      await this.ensureBucketExists(bucketName);

      // Generate object path with UUID directory if enabled
      const finalObjectName = `cases/${nanoid()}/${objectName}`;

      await this.client.putObject(
        bucketName,
        finalObjectName,
        Buffer.from(fileContent),
        fileContent.byteLength,
        {
          'Content-Type': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
        }
      );

      const url = await this.getObjectUrl(bucketName, finalObjectName);
      const presignedUrl = await this.generatePresignedUrl(bucketName, finalObjectName);

      return {
        success: true,
        url,
        presignedUrl,
        objectPath: finalObjectName,
        objectName: basename(finalObjectName),
        message: 'File uploaded successfully to Minio',
      };
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
      logger.error(
        'Minio Uploader: Upload failed',
        createErrorLogContext(error, {
          filePath,
          objectName,
          bucketName,
        })
      );

      return {
        success: false,
        url: '',
        presignedUrl: '',
        objectPath: '',
        objectName: '',
        message: `Upload failed: ${errorMessage}`,
      };
    }
  }

  private async ensureBucketExists(bucketName: string): Promise<void> {
    if (!this.client) return;

    try {
      const exists = await this.client.bucketExists(bucketName);
      if (!exists) {
        await this.client.makeBucket(bucketName, 'us-east-1');
        logger.info('Minio Uploader: Created bucket', { bucketName });
      }
    } catch (error) {
      logger.error(
        'Minio Uploader: Failed to check/create bucket',
        createErrorLogContext(error, {
          bucketName,
        })
      );
      throw error;
    }
  }

  private async getObjectUrl(bucketName: string, objectName: string): Promise<string> {
    if (!this.client) return '';

    try {
      const protocol = process.env.MINIO_USE_SSL === 'true' ? 'https' : 'http';
      const port = process.env.MINIO_PORT || '9000';
      const endpoint = process.env.MINIO_ENDPOINT;

      return `${protocol}://${endpoint}:${port}/${bucketName}/${objectName}`;
    } catch (error) {
      logger.error(
        'Minio Uploader: Failed to generate object URL',
        createErrorLogContext(error, {
          bucketName,
          objectName,
        })
      );
      return '';
    }
  }

  private async generatePresignedUrl(bucketName: string, objectName: string, expirySeconds: number = 3600): Promise<string> {
    if (!this.client) return '';

    try {
      // Generate presigned URL with custom expiry time
      const presignedUrl = await this.client.presignedGetObject(
        bucketName,
        objectName,
        expirySeconds
      );

      return presignedUrl;
    } catch (error) {
      logger.error(
        'Minio Uploader: Failed to generate presigned URL',
        createErrorLogContext(error, {
          bucketName,
          objectName,
          expirySeconds,
        })
      );
      return '';
    }
  }

  isConfigured(): boolean {
    return this.client !== null;
  }

  /**
   * 公开的生成预签名URL方法
   */
  async generatePublicPresignedUrl(
    bucketName: string, 
    objectName: string, 
    expirySeconds: number = 3600
  ): Promise<string> {
    return await this.generatePresignedUrl(bucketName, objectName, expirySeconds);
  }
}

export const minioUploader = new MinioUploader();
